﻿package com.actionpals.components.controls
{
	import com.actionpals.components.controls.calendar.CalendarDateHeader;
	import com.actionpals.components.controls.calendar.CalendarDayNameHeader;
	import com.actionpals.components.controls.calendar.CalendarEventLoader;
	import com.actionpals.components.controls.calendar.CalendarMonth;
	import com.actionpals.components.controls.calendar.CalendarStyle;
	import com.actionpals.events.CalendarEvent;
	import com.actionpals.graphics.RectangleShape;
	import com.actionpals.utils.ArrayUtil;
	
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.net.URLRequest;
	
	public class Calendar extends Sprite
	{
		private var bg:RectangleShape;
		private var dateHeader:CalendarDateHeader;
		private var dayNameHeader:CalendarDayNameHeader;
    	private var month:CalendarMonth;
		private var calendarEventLoader:CalendarEventLoader;
		private var date:Date = new Date();
		private var invalid:Boolean = false;
		
		private var _dayNames:Array = new Array( "S", "M", "T", "W", "T", "F", "S" );
		private var _monthNames:Array = new Array( "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" );
		private var _selectedDate:Date;
		private var _showToday:Boolean = true;
		private var _showVisited:Boolean = false;
		private var _style:CalendarStyle;
		private var _visitedDates:Array = new Array();
		private var _width:Number;
		private var _height:Number;
        
        public function Calendar( width:Number=300, height:Number=300, style:CalendarStyle=null )
        {
        	_width = width;
        	_height = height;
        	_style = style ? style : new CalendarStyle();
        	
			init();
		}
		
		public function get dayNames():Array
		{
			return _dayNames;
		}
		public function set dayNames( value:Array ):void
		{
			_dayNames = value;
			invalidate();
		}
		
		public function get displayedMonth():int
		{
			return date.month;
		}
		public function set displayedMonth( value:int ):void
		{
			date.month = value;
			invalidate();
		}
		
		public function get displayedYear():int
		{
			return date.fullYear;
		}
		public function set displayedYear( value:int ):void
		{
			date.fullYear = value;
			invalidate();
		}
		
		public function get events():Array
		{
			return calendarEventLoader ? calendarEventLoader.events : new Array();
		}
		
		public function get monthNames():Array
		{
			return _monthNames;
		}
		public function set monthNames( value:Array ):void
		{
			_monthNames = value;
			invalidate();
		}
		
		public function get selectedDate():Date
		{
			return _selectedDate;
		}
		public function set selectedDate( value:Date ):void
		{
			_selectedDate = value;
			if( !ArrayUtil.arrayContainsValue( visitedDates, value ) ) visitedDates.push( value );
			drawNow();
		}
		
		public function get showToday():Boolean
		{
			return _showToday;
		}
		public function set showToday( value:Boolean ):void
		{
			_showToday = value;
			invalidate();
		}
		
		public function get showVisited():Boolean
		{
			return _showVisited;
		}
		public function set showVisited( value:Boolean ):void
		{
			_showVisited = value;
			invalidate();
		}
		
		public function get style():CalendarStyle
		{
			return _style;
		}
		public function set style( value:CalendarStyle ):void
		{
			_style = value;
			invalidate();
		}
		
		public function get visitedDates():Array
		{
			return _visitedDates;
		}
		public function set visitedDates( value:Array ):void
		{
			_visitedDates = value;
			invalidate();
		}
		
		public override function get width():Number
		{
			return _width;
		}
		public override function set width( value:Number ):void
		{
			_width = value;
			invalidate();
		}
		
		public override function get height():Number
		{
			return _height;
		}
		public override function set height( value:Number ):void
		{
			_height = value;
			invalidate();
		}
		
		public function clearVisited():void
		{
			visitedDates.length = 0;
			invalidate();
		}
		
		public function drawNow():void
		{
			invalid = false;
			draw();
		}
		
		public function loadEventData( eventDataURL:String, eventDataFormat:String ):void
		{
			calendarEventLoader = new CalendarEventLoader( eventDataFormat );
			calendarEventLoader.load( new URLRequest( eventDataURL ) );
			calendarEventLoader.addEventListener( Event.INIT, onDataInit );
			calendarEventLoader.addEventListener( IOErrorEvent.IO_ERROR, onDataIOError );
		}
		
		public function nextMonth():void
		{
			if( displayedMonth == 11 )
			{
				displayedYear += 1;
				displayedMonth = 0;
			}
			else
			{
				displayedMonth += 1;
			}
			drawNow();
			
			dispatchEvent( new CalendarEvent( CalendarEvent.NEXT_MONTH_SELECT, date, true, true ) );
		}
		
		public function previousMonth():void
		{
			if( displayedMonth == 0 )
			{
				displayedYear -= 1;
				displayedMonth = 11;
			}
			else
			{
				displayedMonth -= 1;
			}
			drawNow();
			
			dispatchEvent( new CalendarEvent( CalendarEvent.PREVIOUS_MONTH_SELECT, date, true, true ) );
		}
		
		public function setEventData( eventData:XML, eventDataFormat:String ):void
		{
			calendarEventLoader = new CalendarEventLoader( eventDataFormat );
			calendarEventLoader.addEventListener( Event.INIT, onDataInit );
			calendarEventLoader.eventData = eventData;
		}
		
		protected function init():void
		{
			addEventListener( Event.ADDED_TO_STAGE, onAddedToStage );
			addEventListener( Event.REMOVED_FROM_STAGE, onRemovedFromStage );
			
			create();
			draw();
		}
		
		protected function create():void
		{
			createBG();
			createDateHeader();
			createDayNameHeader();
		}
		
		protected function createBG():void
		{
			bg = new RectangleShape( 100, 100 );
			addChild( bg );
		}
		
		protected function createDateHeader():void
		{
			dateHeader = new CalendarDateHeader( this );
			dateHeader.addEventListener( CalendarEvent.NEXT_MONTH_SELECT, onNextClick );
			dateHeader.addEventListener( CalendarEvent.PREVIOUS_MONTH_SELECT, onPreviousClick );
			addChild( dateHeader );
		}
		
		protected function createDayNameHeader():void
		{
			dayNameHeader = new CalendarDayNameHeader( this );
			addChild( dayNameHeader );
		}
		
		protected function draw():void
		{
			bg.cornerRadius = style.cornerRadius;
			bg.width = width;
			bg.height = height;
			bg.fillColor = style.backgroundColor;
			bg.fillAlpha = style.backgroundAlpha;
			bg.strokeColor = style.borderColor;
			bg.strokeThickness = style.borderThickness;
			bg.drawNow();
			
			dateHeader.drawNow();
			
			dayNameHeader.drawNow();
			dayNameHeader.y = dateHeader.y + dateHeader.height;
			
			if( month ) removeChild( month );
			month = new CalendarMonth( this, width, ( height - dateHeader.height - dayNameHeader.height - ( ( style.footerPercentHeight / 100 ) * height ) ) );
			month.drawNow();
			addChild( month );
			month.y = dayNameHeader.y + dayNameHeader.height;
		}
		
		protected function invalidate():void
		{
			if( !invalid )
			{
				try
				{
					stage.invalidate();
					invalid = true;
				}
				catch( error:Error )
				{
					invalid = false;
				}
			}
		}
		
		
		/**
		 * @private
		 */	
		protected function onRender( event:Event ):void
		{
			if( invalid )
			{
				draw();
				invalid = false;
			}
		}
		
		/**
		 * @private
		 */	
		protected function onAddedToStage( event:Event ):void
		{
			stage.addEventListener( Event.RENDER, onRender );
			draw();
		}
		
		/**
		 * @private
		 */	
		protected function onRemovedFromStage( event:Event ):void
		{
			removeEventListener( Event.ADDED_TO_STAGE, onAddedToStage );
			removeEventListener( Event.REMOVED_FROM_STAGE, onRemovedFromStage );
		}
		
		/**
		 * @private
		 */	
		protected function onDataInit( evt:Event ):void
		{
			drawNow();
			dispatchEvent( new Event( Event.INIT ) );
		}
		
		/**
		 * @private
		 */	
		protected function onDataIOError( evt:IOErrorEvent ):void
		{
			trace( evt.text );
		}
		
		private function onNextClick( evt:CalendarEvent ):void
		{
			nextMonth();
		}
		
		private function onPreviousClick( evt:CalendarEvent ):void
		{
			previousMonth();
		}
		
    }
}